﻿#include "stdafx.h"
#include "sbcsx.h"

//////////////////////////////////////////////////////////////////////////
// crbs

kObj* kCrbs::Create(kType type) throw()
{
	kBase* base = k_crbs_new(type);
	kObj* obj = static_cast<kObj*>(base);
	return obj;
}

void kCrbs::Delete(kObj* base) throw()
{
	if (base)
		base->Unload();
}

void kCrbs::ForceDelete(kObj* base) throw()
{
	if (base)
		base->ForceUnload();
}


//////////////////////////////////////////////////////////////////////////
// 오브젝트 (베이스)

//
void kObj::TypeInitialize(kType type) throw()
{
	k_type_set_roam(type, K_MAX_USHORT);
}

//
void kObj::TypeFinalize(kType /*type*/) throw()
{
}

//
kint kObj::Load()
{
	return k_base_ref_load(this);
}

//
kint kObj::Unload()
{
	return k_base_ref_unload(this);
}

//
kObj* kObj::PtrLoad()
{
	return (kObj*)k_base_load(this);
}

//
kObj* kObj::PtrUnload()
{
	return (kObj*)k_base_unload(this);
}

//
kvoid kObj::ForceUnload()
{
	k_base_force_unload(this);
}

//
kvoid kObj::ForceRevive()
{
	k_base_force_revive(this);
}

//
kint kObj::GetRef()
{
	return base_ref;
}

//
kType kObj::GetType()
{
	return type_info->supers[0];
}

//
kTypeInfo* kObj::GetTypeInfo()
{
	return type_info;
}

//
kpointer kObj::GetUserData()
{
	return base_data;
}

//
kpointer kObj::SetUserData(kpointer data)
{
	kpointer prev = base_data;
	base_data = data;
	return prev;
}

//
void* kObj::operator new(size_t size, const char* file, int line) throw()
{
	return k_alloc_dbg(size, file, line);
}

//
void* kObj::operator new(size_t size) throw()
{
	return k_alloc(size);
}

//
void* kObj::operator new[ ](size_t size, const char* file, int line) throw()
{
	return k_alloc_dbg(size, file, line);
}

//
void* kObj::operator new[ ](size_t size) throw()
{
	return k_alloc(size);
}

//
void kObj::operator delete(void* ptr, const char*, int) throw()
{
	k_free(ptr);
}

//
void kObj::operator delete(void* ptr) throw()
{
	k_free(ptr);
}

//
void kObj::operator delete[ ](void* ptr, const char*, int) throw()
{
	k_free(ptr);
}

//
void kObj::operator delete[ ](void* ptr) throw()
{
	k_free(ptr);
}


//////////////////////////////////////////////////////////////////////////
// 테스트 오브젝트
KIMPL_SUBCLASS(kObjTest, "TestObj");

kObjTest::kObjTest()
	: t_val(10)
{
}

kObjTest::~kObjTest()
{
	t_val = -1;
}

kint kObjTest::GetValue()
{
	return t_val;
}

void kObjTest::SetValue(kint value)
{
	t_val = value;
}


//////////////////////////////////////////////////////////////////////////
// 오브젝트 해시
class kObjMukumImpl : public kObjMukum
{
public:
	void Resize()
	{
		ksize_t newbucket = k_primeclose(_count);
		kObjNode** newnodes = k_new_zero(newbucket, kObjNode*);

		for (ksize_t i = 0; i < _bucket; i++)
		{
			kObjNode* sib;

			for (kObjNode* node = _nodes[i]; node; node = sib)
			{
				sib = node->_node_sib;

				ksize_t hashmask = node->_node_hash % newbucket;
				node->_node_sib = newnodes[hashmask];
				newnodes[hashmask] = node;
			}
		}

		k_delete(_nodes);

		_nodes = newnodes;
		_bucket = newbucket;
	}

	//
	void Test()
	{
		ksize_t cnt = _count;
		ksize_t bk = _bucket;

		if ((bk >= 3 * cnt && bk > K_MIN_HASH) ||
			(3 * bk <= cnt && bk < K_MAX_HASH))
			Resize();
	}

	//
	void EraseAll(bool isforce)
	{
		kObjNode* node;
		kObjNode* next;

		if (isforce)
		{
			for (ksize_t i = 0; i < _count; i++)
			{
				for (node = _nodes[i]; node; node = next)
				{
					next = node->_node_sib;
					node->ForceUnload();
				}
			}
		}
		else
		{
			for (ksize_t i = 0; i < _count; i++)
			{
				for (node = _nodes[i]; node; node = next)
				{
					next = node->_node_sib;

					if (node->Unload() > 0)
						node->_node_sib = NULL;
				}
			}
		}

		_count = 0;
		_revision++;
		memset(_nodes, 0, _bucket * sizeof(kObjNode*));
	}

	void EraseNode(kObjNode** * ppnode)
	{
		kObjNode** nnode = *ppnode;
		kObjNode* node = *nnode;

		*nnode = node->_node_sib;

		_count--;
		_revision++;
	}

	//
	bool EraseBase(kObjNode* base)
	{
		ksize_t hash;
		kObjNode** rnode;
		kObjNode* node;

		k_return_value_if_fail(base != NULL, false);

		hash = base->_node_hash;
		rnode = &_nodes[hash % _bucket];

		while ((node = *rnode) != NULL)
		{
			if (node == base)
			{
				EraseNode(&rnode);
				return true;
			}

			rnode = &node->_node_sib;
		}

		return false;
	}

	//
	kObjNode* Erase(kconstpointer key)
	{
		kObjNode** rnode;
		kObjNode* node;

		rnode = LookUpKey(key, NULL);
		node = *rnode;

		if (!node)
			return NULL;
		else
		{
			EraseNode(&rnode);
			return node;
		}
	}

	//
	kObjNode** LookUpBase(kObjNode* base, ksize_t* rethash)
	{
		kObjNode** lnode;
		kObjNode* node;
		kconstpointer key;
		ksize_t hash;

		key = _info->func_key((kNodeBase*)base);
		hash = _info->func_hash(key);
		lnode = &_nodes[hash % _bucket];

		while ((node = *lnode) != NULL)
		{
			if (node->_node_hash == hash && _info->func_eq((kNodeBase*)node, key))
				break;

			lnode = &node->_node_sib;
		}

		if (rethash)
			*rethash = hash;

		return lnode;
	}

	//
	kObjNode** LookUpKey(kconstpointer key, ksize_t* rethash)
	{
		kObjNode** lnode;
		kObjNode* node;
		ksize_t hash;

		hash = _info->func_hash(key);
		lnode = &_nodes[hash % _bucket];

		while ((node = *lnode) != NULL)
		{
			if (node->_node_hash == hash && _info->func_eq((kNodeBase*)node, key))
				break;

			lnode = &node->_node_sib;
		}

		if (rethash)
			*rethash = hash;

		return lnode;
	}

	//
	bool Add(kObjNode* node)
	{
		ksize_t hash;
		kObjNode** anode;

		anode = LookUpBase(node, &hash);

		if (*anode)
			return false;
		else
		{
			node->_node_sib = NULL;
			node->_node_hash = hash;
			*anode = node;

			_revision++;
			_count++;

			return true;
		}
	}	
};

void kObjMukum::Clear(bool byforce)
{
	static_cast<kObjMukumImpl*>(this)->EraseAll(byforce);
	static_cast<kObjMukumImpl*>(this)->Test();
}

bool kObjMukum::Add(kObjNode* node)
{
	if (!static_cast<kObjMukumImpl*>(this)->Add(node))
		return false;
	else
	{
		static_cast<kObjMukumImpl*>(this)->Test();
		return true;
	}
}

kObjNode* kObjMukum::Get(kconstpointer key)
{
	kObjNode** gnode = static_cast<kObjMukumImpl*>(this)->LookUpKey(key, NULL);
	return *gnode;
}

kObjNode* kObjMukum::Remove(kconstpointer key)
{
	kObjNode* node = static_cast<kObjMukumImpl*>(this)->Erase(key);

	if (!node)
		return NULL;
	else
	{
		static_cast<kObjMukumImpl*>(this)->Test();
		return node;
	}
}

void kObjMukum::RemoveNode(kObjNode* node)
{
	if (static_cast<kObjMukumImpl*>(this)->EraseBase(node))
		static_cast<kObjMukumImpl*>(this)->Test();
}

